﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace Devonline.Core
{
    /// <summary>
    /// 命令行参数处理工具类
    /// TODO TBC
    /// written by 林选臣
    /// modified by devonline.cn 2020-2-24 17:38:18
    /// 
    /// usage: eg:  --dir /home/app/xxx
    /// var arguments = CommandLineArgumentParser.Parse(args);
    /// var arg = arguments.Get("--dir").Take();
    /// arg => /home/app/xxx
    /// 
    /// 复杂命令行参数请使用 CommandLineParser 库
    /// nuget => Install-Package CommandLineParser
    /// </summary>
    public class CommandLineArgumentParser
    {
        private readonly List<CommandLineArgument> _arguments;

        public CommandLineArgumentParser(string[] args)
        {
            if (args == null)
            {
                throw new ArgumentNullException(nameof(args));
            }

            _arguments = new List<CommandLineArgument>();
            for (var i = 0; i < args.Length; i++)
            {
                _arguments.Add(new CommandLineArgument(_arguments, i, args[i]));
            }
        }

        public IEnumerable<CommandLineArgument> GetEnumerator()
        {
            foreach (var arg in _arguments)
            {
                yield return arg;
            }
        }

        public static CommandLineArgumentParser Parse(string[] args) => new(args);

        public CommandLineArgument Get(string argumentName) => _arguments.FirstOrDefault(p => p == argumentName);

        public T Get<T>(string argumentName) where T : IConvertible
        {
            return (T)Convert.ChangeType(_arguments.FirstOrDefault(p => p == argumentName)?.ToString(), typeof(T), CultureInfo.CurrentCulture);
        }

        public bool Has(string argumentName) => _arguments.Any(p => p == argumentName);
    }

    /// <summary>
    /// 命令行参数
    /// </summary>
    public class CommandLineArgument
    {
        private readonly List<CommandLineArgument> _arguments;
        private readonly string _argumentText;
        private readonly int _index;

        internal CommandLineArgument(List<CommandLineArgument> args, int index, string argument)
        {
            _arguments = args;
            _index = index;
            _argumentText = argument;
        }

        public CommandLineArgument Next => (_index < _arguments.Count - 1) ? _arguments[_index + 1] : null;

        public CommandLineArgument Previous => (_index > 0) ? _arguments[_index - 1] : null;

        public CommandLineArgument Take() => Next;

        public IEnumerable<CommandLineArgument> Take(int count)
        {
            var list = new List<CommandLineArgument>();
            var parent = this;
            for (var i = 0; i < count; i++)
            {
                var next = parent.Next;
                if (next == null)
                    break;

                list.Add(next);
                parent = next;
            }

            return list;
        }

        public static implicit operator string(CommandLineArgument argument) => argument?._argumentText;

        public override string ToString() => _argumentText;
    }
}
